package com.bigfish.gbs.shiro.filter;


import com.alibaba.fastjson.JSON;

import com.bigfish.gbs.dto.Account;
import com.bigfish.gbs.dto.Result;
import com.bigfish.gbs.enums.ResultEnum;
import com.bigfish.gbs.exception.MyException;
import com.bigfish.gbs.service.UserService;
import com.bigfish.gbs.shiro.provider.AccountProvider;
import com.bigfish.gbs.shiro.token.PasswordToken;
import com.bigfish.gbs.util.*;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.StringUtils;

import javax.servlet.Servlet;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/* *
 * @Author Joycessh
 * @Description 基于 用户名密码 的认证过滤器
 * @Date 20:18 2018/2/10
 */
public class PasswordFilter extends AccessControlFilter {

    private static final Logger LOGGER = LoggerFactory.getLogger(PasswordFilter.class);

    private StringRedisTemplate redisTemplate;

    @Autowired
    private AccountProvider accountProvider;

    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {

//        Subject subject = getSubject(request,response);
//        // 如果其已经登录，再此发送登录请求
//        if(null != subject && subject.isAuthenticated()){
//            return true;
//        }
        //  拒绝，统一交给 onAccessDenied 处理
        return false;
    }

    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {

        AuthenticationToken authenticationToken = null;

        try {
            loadUserRole(request);
        }catch (MyException e) {
            LOGGER.info("账户角色匹配失败");
            Result result = ResultUtil.retFormat(ResultEnum.LOAD_ROLE_ERROR);
            RequestResponseUtil.responseWrite(JSON.toJSONString(result),response);
            return false;
        }

        try {
            authenticationToken = createPasswordToken(request);
            System.out.println(authenticationToken);
        }catch (Exception e) {
            // response 告知无效请求
            Result result = ResultUtil.retParams(1111,"error request");
            RequestResponseUtil.responseWrite(JSON.toJSONString(result),response);
            return false;
        }

        Subject subject = getSubject(request, response);
        try {
            System.out.println("subject.login");
            subject.login(authenticationToken);
            //登录认证成功,进入请求派发json web token url资源内
            return true;
        }catch (AuthenticationException e) {
            LOGGER.warn(authenticationToken.getPrincipal()+"::"+e.getMessage());
            // 返回response告诉客户端认证失败
            Result result = ResultUtil.retParams(1004,"login fail");
            RequestResponseUtil.responseWrite(JSON.toJSONString(result),response);
            return false;
        }catch (Exception e) {
            LOGGER.error(authenticationToken.getPrincipal()+"::认证异常::"+e.getMessage(),e);
            // 返回response告诉客户端认证失败
            Result result = ResultUtil.retParams(1004,"login fail");
            RequestResponseUtil.responseWrite(JSON.toJSONString(result),response);
            return false;
        }

    }

    private AuthenticationToken createPasswordToken(ServletRequest request) throws Exception {

        Map<String ,String> map = RequestResponseUtil.getRequestBodyMap(request);
        String appId = map.get("appId");
        Account account = accountProvider.loadAccount(appId);
        String salt = account.getSalt();
        String password = map.get("password");
        String passwordSalt = MD5Util.md5(password+salt);  // 用盐对密码进行MD5加密
        String host = IpUtil.getIpFromRequest(WebUtils.toHttp(request));

        return new PasswordToken(appId,passwordSalt,host);
    }

    /**
     * 匹配账号的角色
     * @author Joycessh
     * @param request
     * @return boolean
     * @date 12/8/18 1:11 PM
     */
    private void loadUserRole(ServletRequest request) throws MyException {

//        boolean flag = false;
        Map<String ,String> map = RequestResponseUtil.getRequestBodyMap(request);
        String appId = map.get("appId");
        String role = map.get("role");
        String uRole = accountProvider.loadAccountRole(appId);

        if (!StringUtils.isEmpty(role)&&role.trim().equals(uRole.trim())) {
            LOGGER.info("账户角色匹配成功");
//            flag = true;
        }else {
            throw new MyException(ResultEnum.LOAD_ROLE_ERROR);
        }
    }

    public void setRedisTemplate(StringRedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

}
